#include <proc.h>
#include <kmalloc.h>
#include <string.h>
#include <sync.h>
#include <pmm.h>
#include <error.h>
#include <sched.h>
#include <elf.h>
#include <vmm.h>
#include <trap.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

/* ------------- process/thread mechanism design&implementation -------------
(an simplified Linux process/thread mechanism )
introduction:
  ucore implements a simple process/thread mechanism. process contains the independent memory sapce, at least one threads
for execution, the kernel data(for management), processor state (for context switch), files(in lab6), etc. ucore needs to
manage all these details efficiently. In ucore, a thread is just a special kind of process(share process's memory).
------------------------------
process state       :     meaning               -- reason
    PROC_UNINIT     :   uninitialized           -- alloc_proc
    PROC_SLEEPING   :   sleeping                -- try_free_pages, do_wait, do_sleep
    PROC_RUNNABLE   :   runnable(maybe running) -- proc_init, wakeup_proc, 
    PROC_ZOMBIE     :   almost dead             -- do_exit

-----------------------------
process state changing:
                                            
  alloc_proc                                 RUNNING
      +                                   +--<----<--+
      +                                   + proc_run +
      V                                   +-->---->--+ 
PROC_UNINIT -- proc_init/wakeup_proc --> PROC_RUNNABLE -- try_free_pages/do_wait/do_sleep --> PROC_SLEEPING --
                                           A      +                                                           +
                                           |      +--- do_exit --> PROC_ZOMBIE                                +
                                           +                                                                  + 
                                           -----------------------wakeup_proc----------------------------------
-----------------------------
process relations
parent:           proc->parent  (proc is children)
children:         proc->cptr    (proc is parent)
older sibling:    proc->optr    (proc is younger sibling)
younger sibling:  proc->yptr    (proc is older sibling)
-----------------------------
related syscall for process:
SYS_exit        : process exit,                           -->do_exit
SYS_fork        : create child process, dup mm            -->do_fork-->wakeup_proc
SYS_wait        : wait process                            -->do_wait
SYS_exec        : after fork, process execute a program   -->load a program and refresh the mm
SYS_clone       : create child thread                     -->do_fork-->wakeup_proc
SYS_yield       : process flag itself need resecheduling, -- proc->need_sched=1, then scheduler will rescheule this process
SYS_sleep       : process sleep                           -->do_sleep 
SYS_kill        : kill process                            -->do_kill-->proc->flags |= PF_EXITING
                                                                 -->wakeup_proc-->do_wait-->do_exit   
SYS_getpid      : get the process's pid

*/

// the process set's list
list_entry_t proc_list;

#define HASH_SHIFT          10
#define HASH_LIST_SIZE      (1 << HASH_SHIFT)
#define pid_hashfn(x)       (hash32(x, HASH_SHIFT))

// has list for process set based on pid
static list_entry_t hash_list[HASH_LIST_SIZE];

// idle proc
struct proc_struct *idleproc = NULL;
// init proc
struct proc_struct *initproc = NULL;
// current proc
struct proc_struct *current = NULL;

static int nr_process = 0;

void kernel_thread_entry(void);
void forkrets(struct trapframe *tf);
void switch_to(struct context *from, struct context *to);

// alloc_proc - alloc a proc_struct and init all fields of proc_struct
//负责分配并返回一个新的struct proc_struct结构，用于存储新建立的内核线程的管理信息。
static struct proc_struct *
alloc_proc(void) {
    struct proc_struct *proc = kmalloc(sizeof(struct proc_struct));
    if (proc != NULL) {
        //LAB4:EXERCISE1 YOUR CODE
        proc->state = PROC_UNINIT;  //设置进程为“未初始化”态  
        proc->pid = -1; //设置进程pid的未初始化值  
        proc->runs = 0;//初始化时间片  
        proc->kstack = 0;//内核栈的地址  
        proc->need_resched = 0;//是否需要调度，初始化设置为0
        proc->parent = NULL;//父节点为空  
        proc->mm = NULL; //内存管理初始化  
        memset(&(proc->context), 0, sizeof(struct context));//进程上下文初始化  
        proc->tf = NULL; //中断帧指针置为空，总是能够指向中断前的trapframe  
        proc->cr3 = boot_cr3;//设置内核页目录表的基址  
        proc->flags = 0; //标志位初始化  
        memset(proc->name, 0, PROC_NAME_LEN); //进程名初始化 
    }
    return proc;
}

// set_proc_name - set the name of proc
char *
set_proc_name(struct proc_struct *proc, const char *name) {
    memset(proc->name, 0, sizeof(proc->name));
    return memcpy(proc->name, name, PROC_NAME_LEN);
}

// get_proc_name - get the name of proc
char *
get_proc_name(struct proc_struct *proc) {
    static char name[PROC_NAME_LEN + 1];
    memset(name, 0, sizeof(name));
    return memcpy(name, proc->name, PROC_NAME_LEN);
}

// get_pid - alloc a unique pid for process
static int
get_pid(void) {
    static_assert(MAX_PID > MAX_PROCESS);
    struct proc_struct *proc;
    list_entry_t *list = &proc_list, *le;
    static int next_safe = MAX_PID, last_pid = MAX_PID;
    if (++ last_pid >= MAX_PID) { 
        last_pid = 1;
        goto inside;
    }
    if (last_pid >= next_safe) {
    inside:
        next_safe = MAX_PID;
    repeat:
        le = list;
        while ((le = list_next(le)) != list) {
            proc = le2proc(le, list_link);
            if (proc->pid == last_pid) {          //确保不存在任何进程的pid和last_pid相同 
                if (++ last_pid >= next_safe) {
                    if (last_pid >= MAX_PID) {
                        last_pid = 1;
                    }
                    next_safe = MAX_PID;
                    goto repeat;
                }
            }
            else if (proc->pid > last_pid && next_safe > proc->pid) {
                next_safe = proc->pid;
            }
        }
    }
    return last_pid;
}

// proc_run - make process "proc" running on cpu
// NOTE: before call switch_to, should load  base addr of "proc"'s new PDT

// 进行线程调度，今当前占有CPu的让出CPU，并命参数proc指向的线程获得CPu控制权
void
proc_run(struct proc_struct *proc) {
    if (proc != current) { // 只有当proc不是当前执行的线程时，才需要执行
        bool intr_flag;
        struct proc_struct *prev = current, *next = proc;
        local_intr_save(intr_flag);// 切换时新线程任务时需要暂时关闭中断，避免出现嵌套中断
        {
            current = proc;
            load_esp0(next->kstack + KSTACKSIZE);//设置Tss任务状态段的espo的值，令其指向新线程的栈顶
            lcr3(next->cr3);// 设置cr3寄存器的值，命其指向新线程的页表
            switch_to(&(prev->context), &(next->context));//switch_to用于完整的进程上下文切换，定义在统一目录下的switch.s中
        }
        local_intr_restore(intr_flag);
    }
}

// forkret -- the first kernel entry point of a new thread/process
// NOTE: the addr of forkret is setted in copy_thread function
//       after switch_to, the current proc will execute here.
static void
forkret(void) {
    forkrets(current->tf);
}

// hash_proc - add proc into proc hash_list
static void
hash_proc(struct proc_struct *proc) {
    list_add(hash_list + pid_hashfn(proc->pid), &(proc->hash_link));
}

// find_proc - find proc frome proc hash_list according to pid
struct proc_struct *
find_proc(int pid) {
    if (0 < pid && pid < MAX_PID) {
        list_entry_t *list = hash_list + pid_hashfn(pid), *le = list;
        while ((le = list_next(le)) != list) {
            struct proc_struct *proc = le2proc(le, hash_link);
            if (proc->pid == pid) {
                return proc;
            }
        }
    }
    return NULL;
}

// kernel_thread - create a kernel thread using "fn" function
// NOTE: the contents of temp trapframe tf will be copied to 
//       proc->tf in do_fork-->copy_thread function
//通过调用kernel_thread函数创建了一个内核线程init_main。
int
kernel_thread(int (*fn)(void *), void *arg, uint32_t clone_flags) {
    struct trapframe tf;
    memset(&tf, 0, sizeof(struct trapframe));
    tf.tf_cs = KERNEL_CS;//代码段是在内核里
    tf.tf_ds = tf.tf_es = tf.tf_ss = KERNEL_DS;//数据段在内核里
    tf.tf_regs.reg_ebx = (uint32_t)fn;//fn代表实际的入口地址/* ebx指向函数地址 */
    tf.tf_regs.reg_edx = (uint32_t)arg;/* edx指向参数 */ 
    tf.tf_eip = (uint32_t)kernel_thread_entry;
    return do_fork(clone_flags | CLONE_VM, 0, &tf);
}

// setup_kstack - alloc pages with size KSTACKPAGE as process kernel stack
static int
setup_kstack(struct proc_struct *proc) {
    struct Page *page = alloc_pages(KSTACKPAGE);
    if (page != NULL) {
        proc->kstack = (uintptr_t)page2kva(page);
        return 0;
    }
    return -E_NO_MEM;
}

// put_kstack - free the memory space of process kernel stack
static void
put_kstack(struct proc_struct *proc) {
    free_pages(kva2page((void *)(proc->kstack)), KSTACKPAGE);
}

// copy_mm - process "proc" duplicate OR share process "current"'s mm according clone_flags
//         - if clone_flags & CLONE_VM, then "share" ; else "duplicate"
static int
copy_mm(uint32_t clone_flags, struct proc_struct *proc) {
    assert(current->mm == NULL);
    /* do nothing in this project */
    return 0;
}

// copy_thread - setup the trapframe on the  process's kernel stack top and
//             - setup the kernel entry point and stack of process
static void
copy_thread(struct proc_struct *proc, uintptr_t esp, struct trapframe *tf) {
    //在内核堆栈的顶部设置中断帧大小的一块栈空间
    proc->tf = (struct trapframe *)(proc->kstack + KSTACKSIZE) - 1;
    *(proc->tf) = *tf;//拷贝在kernel_thread函数建立的临时中断帧的初始值
    proc->tf->tf_regs.reg_eax = 0;
    //设置子进程/线程执行完do_fork后的返回值
    proc->tf->tf_esp = esp;//设置中断帧中的栈指针esp
    proc->tf->tf_eflags |= FL_IF;//使能中断

    proc->context.eip = (uintptr_t)forkret;//完成对返回中断的一个处理过程
    proc->context.esp = (uintptr_t)(proc->tf);//当前的栈顶指针指向proc->tf
}

/* do_fork -     parent process for a new child process
 * @clone_flags: used to guide how to clone the child process
 * @stack:       the parent's user stack pointer. if stack==0, It means to fork a kernel thread.
 * @tf:          the trapframe info, which will be copied to child process's proc->tf
 */
 //实现具体的尤其针对init_proc的内核进程控制块的初始化
int  
do_fork(uint32_t clone_flags, uintptr_t stack, struct trapframe *tf) {  
    int ret = -E_NO_FREE_PROC;  
    struct proc_struct *proc;  
    if (nr_process >= MAX_PROCESS) {  
        goto fork_out;  
    }  
    ret = -E_NO_MEM;  

    //第一步：申请内存块，如果失败，直接返回处理  
    if ((proc = alloc_proc()) == NULL) {  
        goto fork_out;  
    }  
    //将子进程的父节点设置为当前进程  
    proc->parent = current;  
    //第二步：为进程分配一个内核栈  
    if (setup_kstack(proc) != 0) {  
        goto bad_fork_cleanup_proc;  
    }  
    //第三步：复制父进程的内存信息到子进程  
    if (copy_mm(clone_flags, proc) != 0) {  
        goto bad_fork_cleanup_kstack;  
    }  
    //第四步：复制父进程相关寄存器信息（上下文）  
    copy_thread(proc, stack, tf);  
    //第五步：将新进程添加到进程列表（此过程需要加保护锁）  
    bool intr_flag;  
    local_intr_save(intr_flag);  //屏蔽中断，intr_flag置为1
    {  
        proc->pid = get_pid();//获取当前进程pid
        hash_proc(proc);//建立hash映射  
        list_add(&proc_list, &(proc->list_link));//加入进程链表
        nr_process ++;//进程数+1  
    }  
    local_intr_restore(intr_flag); //恢复中断  
    wakeup_proc(proc);//唤醒新进程
    ret = proc->pid;//设置返回的子进程号  
        
    fork_out:  //分配进程数大于4096
        return ret;  

    bad_fork_cleanup_kstack:  //分配内核栈失败
        put_kstack(proc);  
    bad_fork_cleanup_proc:  
        kfree(proc);  
        goto fork_out;  
}  

// do_exit - called by sys_exit
//   1. call exit_mmap & put_pgdir & mm_destroy to free the almost all memory space of process
//   2. set process' state as PROC_ZOMBIE, then call wakeup_proc(parent) to ask parent reclaim itself.
//   3. call scheduler to switch to other process
int
do_exit(int error_code) {
    panic("process exit!!.\n");
}

// init_main - the second kernel thread used to create user_main kernel threads
static int
init_main(void *arg) {
    cprintf("this initproc, pid = %d, name = \"%s\"\n", current->pid, get_proc_name(current));
    cprintf("To U: \"%s\".\n", (const char *)arg);
    cprintf("To U: \"en.., Bye, Bye. :)\"\n");
    return 0;
}

// proc_init - set up the first kernel thread idleproc "idle" by itself and 
//           - create the second kernel thread init_main
//用于创建第0个线程
void
proc_init(void) {
    int i;

    list_init(&proc_list);//定义在list.h，初始化线程控制块链表。
    for (i = 0; i < HASH_LIST_SIZE; i ++) {//初始化哈希表
        list_init(hash_list + i);
    }

    if ((idleproc = alloc_proc()) == NULL) {//为idleproc分配空间
        panic("cannot alloc idleproc.\n");
    }

    idleproc->pid = 0;//第0个进程：idleproc
    idleproc->state = PROC_RUNNABLE;//当前处于就绪态
    idleproc->kstack = (uintptr_t)bootstack;//给他设置一个内核堆栈
    idleproc->need_resched = 1;//设置为需要被调度
    set_proc_name(idleproc, "idle");//设置名字
    nr_process ++;//进程数加一

    current = idleproc;//当前进程为0号进程：idleproc

    int pid = kernel_thread(init_main, "Hello world!!", 0);//kernel_thread函数的作用是产生一个新的线程
    if (pid <= 0) {
        panic("create init_main failed.\n");
    }

    initproc = find_proc(pid);
    set_proc_name(initproc, "init");

    assert(idleproc != NULL && idleproc->pid == 0);
    assert(initproc != NULL && initproc->pid == 1);
}

// cpu_idle - at the end of kern_init, the first kernel thread idleproc will do below works
//去查找当前的idle_proc是否是需要调度的
void
cpu_idle(void) {
    while (1) {//不断的循环，直到发现有其他线程可以被调度
        if (current->need_resched) {
            schedule();
        }
    }
}

